part of '../widgets.dart';

typedef VanFieldErrorMessageBuilder = dynamic Function(VanFieldValidateScope scope);

/// 用户可以在文本框内输入或编辑文字。
class VanField extends StatefulWidget {
  /// props - 初始值/当前输入的值
  final String? value;

  /// props - 控制器
  final VanCtrl<String>? ctrl;

  /// props - 名称，作为提交表单时的标识符
  final String? name;

  /// props - 输入框 id，同时会设置 label 的 for 属性(默认: van-field-n-input)
  final String id;

  /// props - 输入框类型, 支持原生 input 标签的所有 type 属性，额外支持了 digit 类型(默认: text)
  final VanFieldType type;

  /// props - 大小，可选值为 large normal
  final String? size;

  /// props - [type:textarea]规定文本区域的高度（以行数计）。默认值是 2。
  final int? rows;

  /// props - 输入的最大字符数
  final int? maxLength;

  /// props - 输入框占位提示文字
  final String? placeholder;

  /// props - 是否显示内边框(默认: true)
  final bool border;

  /// props - 是否禁用输入框(默认: false)
  final bool disabled;

  /// props - 是否为只读状态，只读状态下无法输入内容(默认: false)
  final bool readonly;

  /// props - 是否在 label 后面添加冒号(默认: false)
  final bool colon;

  /// props - 是否显示表单必填星号
  final bool? required;

  /// props - 是否使内容垂直居中(默认: false)
  final bool center;

  /// props - 是否启用清除图标，点击清除图标后会清空输入框(默认: false)
  final bool clearable;

  /// props - 清除图标名称或图片链接，等同于 Icon 组件的 name 属性(默认: clear)
  final IconData clearIcon;

  /// props - 显示清除图标的时机，always 表示输入框不为空时展示，focus 表示输入框聚焦且不为空时展示(默认: focus)
  final VanFieldClearTrigger clearTrigger;

  /// props - 是否开启点击反馈(默认: false)
  final bool clickable;

  /// props - 是否展示右侧箭头并开启点击反馈(默认: false)
  final bool isLink;

  /// props - 是否自动聚焦，iOS 系统不支持该属性(默认: false)
  final bool autofocus;

  /// props - 是否显示字数统计，需要设置 maxlength 属性(默认: false)
  final bool showWordLimit;

  /// props - 是否将输入内容标红(默认: false)
  final bool error;

  /// props - 错误提示文案对齐方式，可选值为 center right(默认: left)
  final VanFieldErrorMessageAlign errorMessageAlign;

  /// props - 输入内容格式化函数
  final String Function(String)? formatter;

  /// props - 格式化函数触发的时机，可选值为 onBlur(默认: onChange)
  final VanFieldFormatTrigger formatTrigger;

  /// props - 箭头方向，可选值为 left up down(默认: right)
  final VanFieldArrowDirection arrowDirection;

  /// props - 左侧文本宽度，默认单位为 px(默认: 6.2em)
  final double? labelWidth;

  /// props - 左侧文本对齐方式，可选值为 center right top(默认: left)
  final VanFieldLabelAlign labelAlign;

  /// props - 输入框对齐方式，可选值为 center right(默认: left)
  final VanFieldInputAlign inputAlign;

  /// props - 是否自适应内容高度，只对 textarea 有效，可传入对象,如 { minRows: 1, maxRows: null }，单位为px(默认: false)
  /// <br>type - [bool] or [VanFieldAutoSizeConfig] or [Map]
  final dynamic autosize;

  /// props - 表单校验规则，详见 Form 组件
  /// <br> **list - type** support **[Map]** or **[VanFieldRule]**
  final List? rules;

  final VanFieldStyle? style;

  /// events - 输入框内容变化时触发
  final Function(String)? onChange;

  /// events - 输入框获得焦点时触发
  final VoidCallback? onFocus;

  /// events - 输入框失去焦点时触发
  final VoidCallback? onBlur;

  /// events - 点击清除按钮时触发
  final VoidCallback? onClear;

  /// events - 点击组件时触发
  final VoidCallback? onClick;

  /// events - 点击输入区域时触发
  final VoidCallback? onClickInput;

  /// events - 点击左侧图标时触发
  final VoidCallback? onClickLeftIcon;

  /// events - 点击右侧图标时触发
  final VoidCallback? onClickRightIcon;

  /// events - 开始表单校验时触发
  final VoidCallback? onStartValidate;

  /// events - 结束表单校验时触发
  final Function? onEndValidate;

  /// props - 输入框左侧文本
  /// slots - 自定义输入框左侧文本
  final dynamic label;

  /// slots - 自定义输入框，使用此插槽后，与输入框相关的属性和事件将失效
  final dynamic input;

  /// props - 左侧图标名称或图片链接，等同于 Icon 组件的 name 属性
  /// slots - 自定义输入框头部图标
  final dynamic leftIcon;

  /// props - 右侧图标名称或图片链接，等同于 Icon 组件的 name 属性
  /// slots - 自定义输入框尾部图标
  final dynamic rightIcon;

  /// slots - 自定义输入框尾部按钮
  final dynamic button;

  /// props - 底部错误提示文案，为空时不展示
  /// slots - 自定义底部错误提示文案
  final dynamic errorMessage;

  /// slots - 自定义输入框最右侧的额外内容
  final dynamic extra;

  const VanField({
    super.key,
    this.value,
    this.ctrl,
    this.name,
    this.id = "van-field-n-input",
    this.type = VanFieldType.text,
    this.size,
    this.rows,
    this.maxLength,
    this.placeholder,
    this.border = true,
    this.disabled = false,
    this.readonly = false,
    this.colon = false,
    this.required,
    this.center = false,
    this.clearable = false,
    this.clearIcon = Icons.highlight_remove,
    this.clearTrigger = VanFieldClearTrigger.focus,
    this.clickable = false,
    this.isLink = false,
    this.autofocus = false,
    this.showWordLimit = false,
    this.error = false,
    this.errorMessageAlign = VanFieldErrorMessageAlign.left,
    this.formatter,
    this.formatTrigger = VanFieldFormatTrigger.onChange,
    this.arrowDirection = VanFieldArrowDirection.right,
    this.labelWidth,
    this.labelAlign = VanFieldLabelAlign.left,
    this.inputAlign = VanFieldInputAlign.left,
    this.autosize = false,
    this.rules,
    this.style,
    this.onChange,
    this.onFocus,
    this.onBlur,
    this.onClear,
    this.onClick,
    this.onClickInput,
    this.onClickLeftIcon,
    this.onClickRightIcon,
    this.onStartValidate,
    this.onEndValidate,
    this.label = "文本",
    this.input,
    this.leftIcon,
    this.rightIcon,
    this.button,
    this.errorMessage,
    this.extra,
  });

  @override
  State<VanField> createState() => _VanFieldState();
}

class _VanFieldState extends State<VanField> {
  late TextEditingController _textEditingController;
  VanVisibleController? _passwordVisibleController;
  late Ref<bool> _passwordOpenedController;
  VanVisibleController? _clearVisibleController;
  late FocusNode _focusNode;
  int passwordValueLengthPrev = 0;
  final Ref<VanFieldValidateScope> _errorMessageScopeRef = ref(const VanFieldValidateScope());

  VanFormState? _form;

  @override
  void initState() {
    super.initState();
    _textEditingController = TextEditingController(text: widget.ctrl?.value ?? widget.value ?? "");
    _focusNode = FocusNode();
    if (widget.onFocus != null || widget.onBlur != null) {
      _focusNode.addListener(() {
        if (_focusNode.hasFocus) {
          if (widget.onFocus != null) widget.onFocus!();
        } else {
          if (widget.onBlur != null) widget.onBlur!();
        }
      });
    }

    _passwordOpenedController = VanCtrl(false);
    if (widget.type == VanFieldType.password) {
      _passwordVisibleController = VanVisibleController(false);
      _focusNode.addListener(() {
        _passwordVisibleController!.visible = false;
        if (_focusNode.hasFocus) {
          passwordValueLengthPrev = _textEditingController.text.trim().length;
        }
      });
    }

    if (widget.clearable) {
      _clearVisibleController = VanVisibleController(widget.clearTrigger == VanFieldClearTrigger.always && _textEditingController.text.isNotEmpty);
      if (widget.clearTrigger != VanFieldClearTrigger.always) {
        _focusNode.addListener(() {
          if (!_focusNode.hasFocus) {
            _clearVisibleController!.visible = false;
          } else {
            _clearVisibleController!.visible = _textEditingController.text.isNotEmpty;
          }
        });
      }
    }

    if (widget.ctrl != null) {
      widget.ctrl!.addListener(() {
        if (widget.ctrl!.value == _textEditingController.text) return;
        _textEditingController.text = widget.ctrl!.value;
      });
    }

    _textEditingController.addListener(() {
      if (widget.formatter != null && widget.formatTrigger == VanFieldFormatTrigger.onChange) {
        if (_triggerFormatText()) return;
      }

      if (widget.clearTrigger == VanFieldClearTrigger.always) {
        _clearVisibleController!.visible = _textEditingController.text.isNotEmpty;
      }

      if (widget.ctrl != null) {
        if (widget.ctrl!.value != _textEditingController.text) return;
        widget.ctrl!.value = _textEditingController.text;
      }
    });

    if (widget.autofocus) {
      Future.delayed(Duration.zero, () => _focusNode.requestFocus());
    }

    Future.delayed(Duration.zero, () {
      _form = VanForm.of(context);
      if (_form == null) return;
      String? name = widget.name;
      if (name?.isNotEmpty != true && widget.label is String) {
        name = widget.label;
      }
      if (name?.isNotEmpty != true && widget.placeholder is String) {
        name = widget.placeholder;
      }
      _form!.addFieldValidation(
        name: name ?? "",
        rules: widget.rules,
        scope: (scope) => _errorMessageScopeRef.value = scope,
        clear: () {
          _textEditingController.text = '';
        },
        value: () {
          return _textEditingController.text;
        },
      );
    });
  }

  @override
  void dispose() {
    _textEditingController.dispose();
    super.dispose();
  }

  bool _triggerFormatText() {
    if (widget.formatter == null) return false;
    final text = _textEditingController.text;
    final formatText = widget.formatter!(text);
    if (formatText != text) {
      _textEditingController.text = formatText;
      return true;
    }
    return false;
  }

  VanFieldStyle getComputedStyle(BuildContext context) {
    final style = widget.style;
    if (style?.isNonNull == true) return style!;
    double? labelWidth = widget.labelWidth ?? style?.labelWidth;
    Color? labelColor = style?.labelColor;
    double? labelMarginRight = style?.labelMarginRight;
    Color? inputTextColor = style?.inputTextColor;
    Color? inputErrorTextColor = style?.inputErrorTextColor;
    Color? placeholderTextColor = style?.placeholderTextColor;
    double? iconSize = style?.iconSize;
    double? clearIconSize = style?.clearIconSize;
    Color? clearIconColor = style?.clearIconColor;
    Color? rightIconColor = style?.rightIconColor;
    Color? errorMessageColor = style?.errorMessageColor;
    double? errorMessageFontSize = style?.errorMessageFontSize;
    double? textAreaMinHeight = style?.textAreaMinHeight;
    Color? wordLimitColor = style?.wordLimitColor;
    double? wordLimitFontSize = style?.wordLimitFontSize;
    double? wordLimitLineHeight = style?.wordLimitLineHeight;
    Color? textColor = style?.textColor;
    Color? requiredMarkColor = style?.requiredMarkColor;

    if (widget.disabled) {
      labelColor ??= kVanFieldDisabledTextColor.toColor(context);
      inputTextColor ??= kVanFieldDisabledTextColor.toColor(context) ?? labelColor;
    }
    if (widget.error) {
      inputTextColor ??= kVanFieldInputErrorTextColor.toColor(context);
      placeholderTextColor ??= inputTextColor;
    }

    labelWidth ??= kVanFieldLabelWidth.toDouble(context);
    labelColor ??= kVanFieldLabelColor.toColor(context);
    labelMarginRight ??= kVanFieldLabelMarginRight.toDouble(context);
    inputTextColor ??= kVanFieldInputTextColor.toColor(context);
    inputErrorTextColor ??= kVanFieldInputErrorTextColor.toColor(context);
    placeholderTextColor ??= kVanFieldPlaceholderTextColor.toColor(context);
    iconSize ??= kVanFieldIconSize.toDouble(context);
    clearIconSize ??= kVanFieldClearIconSize.toDouble(context);
    clearIconColor ??= kVanFieldClearIconColor.toColor(context);
    rightIconColor ??= kVanFieldRightIconColor.toColor(context);
    errorMessageColor ??= kVanFieldErrorMessageColor.toColor(context);
    errorMessageFontSize ??= kVanFieldErrorMessageFontSize.toDouble(context);
    textAreaMinHeight ??= kVanFieldTextAreaMinHeight.toDouble(context);
    wordLimitColor ??= kVanFieldWordLimitColor.toColor(context);
    wordLimitFontSize ??= kVanFieldWordLimitFontSize.toDouble(context);
    wordLimitLineHeight ??= kVanFieldWordLimitLineHeight.toDouble(context);
    // textColor ??= kVanFieldTextColor.get(context);
    requiredMarkColor ??= kVanFieldRequiredMarkColor.toColor(context);

    return VanFieldStyle(
      labelWidth: labelWidth,
      labelColor: labelColor,
      labelMarginRight: labelMarginRight,
      inputTextColor: inputTextColor,
      inputErrorTextColor: inputErrorTextColor,
      placeholderTextColor: placeholderTextColor,
      iconSize: iconSize,
      clearIconSize: clearIconSize,
      clearIconColor: clearIconColor,
      rightIconColor: rightIconColor,
      errorMessageColor: errorMessageColor,
      errorMessageFontSize: errorMessageFontSize,
      textAreaMinHeight: textAreaMinHeight,
      wordLimitColor: wordLimitColor,
      wordLimitFontSize: wordLimitFontSize,
      wordLimitLineHeight: wordLimitLineHeight,
      textColor: textColor,
      requiredMarkColor: requiredMarkColor,
    );
  }

  Widget _buildLabel(BuildContext context, VanFieldStyle style) {
    bool? required = widget.required ?? VanForm.of(context)?.widget.required;
    if (required != false && widget.rules != null) {
      final rules = VanFieldRule.fromList(widget.rules!);
      for (var rule in rules) {
        if (rule.required != true) continue;
        required = true;
        break;
      }
    }
    return LayoutBuilder(builder: (context, _) {
      final List<Widget> children0 = [];
      final fontSize = DefaultTextStyle.of(context).style.fontSize ?? 14;
      if (required == true) {
        children0.add(Padding(
          padding: const EdgeInsets.only(right: 2),
          child: Text("*", style: TextStyle(fontSize: fontSize, color: style.requiredMarkColor)),
        ));
      }

      Widget? label0 = createWidget(widget.label, type: runtimeType, tag: "label");

      children0.add(label0 ?? const Text(":"));
      if (label0 != null && widget.colon) children0.add(const Text(":"));
      Widget child0 = Row(
        mainAxisAlignment: widget.labelAlign.mainAxisAlignment,
        mainAxisSize: MainAxisSize.max,
        children: children0,
      );

      child0 = DefaultTextStyle(
        style: TextStyle(fontSize: fontSize, color: style.labelColor),
        child: child0,
      );

      if (widget.labelAlign == VanFieldLabelAlign.top) {
        child0 = Container(
          margin: EdgeInsets.only(bottom: kVanPaddingBase.toDouble(context) ?? 0),
          child: child0,
        );
      }

      return child0;
    });
  }

  Widget _buildInput(BuildContext context, VanFieldStyle style) {
    final String? hintText = widget.placeholder ?? (widget.label is String ? "请输入${widget.label}" : null);
    final fontSize = DefaultTextStyle.of(context).style.fontSize;

    int? maxLines = 1;
    int minLines = 1;
    if (widget.type == VanFieldType.textarea) {
      minLines = widget.rows ?? 2;

      if (widget.autosize != null) {
        VanFieldAutoSizeConfig? size;
        if (widget.autosize is bool) {
          if (widget.autosize == true) maxLines = null;
        } else if (widget.autosize is Map) {
          size = VanFieldAutoSizeConfig.fromJson(widget.autosize);
        } else if (widget.autosize is VanFieldAutoSizeConfig) {
          size = widget.autosize;
        } else {
          maxLines = minLines;
          _logger.e("${widget.runtimeType}.autosize only type support [bool] or [VanFieldAutoSize] or [Map].");
        }

        final minRows = size?.minRows ?? 0;
        final maxRows = size?.maxRows ?? 0;
        if (minRows > 0) minLines = minRows;
        if (maxRows > 0) maxLines = maxRows;

        if (maxLines != null && minLines > maxLines) maxLines = minLines;
      }
    }
    return ValueListenableBuilder(
        valueListenable: _passwordOpenedController,
        builder: (context, opened, _) {
          return TextField(
            controller: _textEditingController,
            keyboardType: widget.type.keyboardType,
            focusNode: _focusNode,
            obscureText: widget.type == VanFieldType.password && !opened,
            style: TextStyle(color: style.inputTextColor, fontSize: fontSize),
            readOnly: widget.readonly,
            enabled: !widget.disabled,
            scrollPadding: EdgeInsets.zero,
            cursorColor: style.inputTextColor,
            cursorWidth: 1,
            maxLength: widget.maxLength,
            maxLines: maxLines,
            minLines: minLines,
            autocorrect: false,
            textAlign: widget.inputAlign.textAlign,
            decoration: InputDecoration(
              isDense: true,
              contentPadding: EdgeInsets.zero,
              hintText: hintText,
              border: InputBorder.none,
              hintStyle: TextStyle(
                fontSize: fontSize,
                color: style.placeholderTextColor,
                textBaseline: TextBaseline.ideographic,
              ),
              error: Align(
                alignment: widget.errorMessageAlign.alignment,
                child: _buildErrorMessage(context, style),
              ),
            ),
            buildCounter: _buildInputCounter,
            onTap: widget.onClickInput,
            onChanged: (value) {
              if (_clearVisibleController != null) {
                _clearVisibleController!.visible = value.isNotEmpty;
              }
              if (_passwordVisibleController != null) {
                final length = value.trim().length;
                if (length < passwordValueLengthPrev) {
                  _passwordVisibleController!.visible = false;
                } else if (length > 0 && passwordValueLengthPrev <= 0) {
                  _passwordVisibleController!.visible = true;
                }
                passwordValueLengthPrev = length;
              }
            },
          );
        });
  }

  Widget? _buildErrorMessage(BuildContext context, VanFieldStyle style) {
    return ValueListenableBuilder(
        valueListenable: _errorMessageScopeRef,
        builder: (context, scope, _) {
          var message = widget.errorMessage;
          if (message is String && scope.message.isNotEmpty) {
            message = scope.message;
          } else if (message is VanFieldErrorMessageBuilder) {
            message = message(scope);
          } else if (message is Widget) {
            message = VanProvider(provide: scope, child: message);
          } else {
            message = scope.message;
          }
          if (message is String && message.isEmpty) {
            return SizedBox(height: 0, width: 0);
          }

          TextStyle? style0 = TextStyle(
            color: style.errorMessageColor,
            fontSize: style.errorMessageFontSize,
          );

          Widget child0 = createWidget(
            message,
            style: style0,
            type: runtimeType,
            tag: "errorMessage",
          )!;

          return child0;
        });
  }

  Widget? _buildInputCounter(
    BuildContext context, {
    required int currentLength,
    required int? maxLength,
    required bool isFocused,
  }) {
    if (!widget.showWordLimit) return null;
    if (maxLength == null || maxLength <= 0) return null;
    return Text("$currentLength/$maxLength");
  }

  Widget? _buildClearIconButton(BuildContext context, VanFieldStyle style) {
    if (_clearVisibleController == null) return null;
    Widget clear = GestureDetector(
      onTap: () {
        _textEditingController.clear();
        if (widget.onClear != null) widget.onClear!();
      },
      child: Icon(widget.clearIcon, size: style.clearIconSize, color: style.clearIconColor),
    );
    clear = Padding(
      padding: EdgeInsets.only(left: kVanPaddingXs.toDouble(context) ?? 0),
      child: clear,
    );

    clear = ValueListenableBuilder(
      valueListenable: _clearVisibleController!,
      child: clear,
      builder: (ctx, visible, child) {
        return AnimatedOpacity(
          opacity: visible ? 1.0 : 0.0,
          duration: Duration(milliseconds: kVanDurationBase.toInt(context) ?? 300),
          child: child,
        );
      },
    );

    return clear;
  }

  @override
  Widget build(BuildContext context) {
    final group = VanProvider.of<VanCellGroup>(context);
    final border = group?.children?.last != widget;

    final style = getComputedStyle(context);
    final cellStyle = VanCellStyle(
      labelWidth: style.labelWidth,
      titleAlignment: widget.labelAlign.alignment,
    );
    return VanCell(
      layout: widget.labelAlign.layout,
      style: cellStyle,
      icon: widget.leftIcon,
      rightIcon: widget.rightIcon,
      title: _buildLabel(context, style),
      value: _buildValue(context, style),
      center: widget.center,
      isLink: widget.isLink,
      arrowDirection: widget.arrowDirection,
      border: border,
      clickable: widget.clickable,
      onClick: widget.onClick,
    );
  }

  Widget _buildValue(BuildContext context, VanFieldStyle style) {
    List<Widget> children0 = [];

    if (widget.labelAlign != VanFieldLabelAlign.top) {
      children0.add(SizedBox(width: style.labelMarginRight ?? 0));
    }

    final input = createWidget(widget.input, type: runtimeType, tag: 'input') ?? _buildInput(context, style);
    children0.add(Expanded(child: input));

    if (_clearVisibleController != null) {
      _buildClearIconButton(context, style);
    }
    if (_passwordVisibleController != null) {
      children0.add(_buildPasswordButton(style));
    }

    if (widget.button != null) {
      Widget? button0 = createWidget(widget.button, type: runtimeType, tag: 'button');
      if (button0 != null) {
        children0.add(Padding(
          padding: EdgeInsets.only(left: kVanPaddingXs.toDouble(context) ?? 0),
          child: button0,
        ));
      }
    }
    return Row(children: children0);
  }

  ValueListenableBuilder<bool> _buildPasswordButton(VanFieldStyle style) {
    return ValueListenableBuilder(
      valueListenable: _passwordVisibleController!,
      child: ValueListenableBuilder(
          valueListenable: _passwordOpenedController,
          builder: (ctx, opened, _) {
            return GestureDetector(
              onTap: () {
                _passwordOpenedController.value = !_passwordOpenedController.value;
                _passwordVisibleController!.visible = true;
              },
              child: Icon(
                opened ? Icons.remove_red_eye : Icons.remove_red_eye_outlined,
                color: style.clearIconColor,
                size: style.clearIconSize,
              ),
            );
          }),
      builder: (ctx, visible, child) {
        return AnimatedOpacity(
          opacity: visible ? 1.0 : 0.0,
          duration: const Duration(milliseconds: 300),
          child: child,
        );
      },
    );
  }
}
